﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Automation;
using System.Threading;
using System.Windows;
using Tedds_Tool.Automatables;
using LEET.LEETCommon.AutomationAbstractions;
using LEET.Exceptions.Automation;

namespace LEET.LEETCommon.WidgetLookup
{
    public class WidgetRetriever
    {
        #region Attributes

        /// <summary>
        /// Automation element search condition to assert element is on the screen and has a non-empty name
        /// </summary>
        private static AndCondition nempty = new AndCondition(new PropertyCondition(AutomationElement.IsOffscreenProperty, false), new NotCondition(new PropertyCondition(AutomationElement.NameProperty, "")));

        /// <summary>
        /// Automation element search condition to assert element is on the screen and has a non-empty id
        /// </summary>
        private static AndCondition idempty = new AndCondition(new PropertyCondition(AutomationElement.IsOffscreenProperty, false), new NotCondition(new PropertyCondition(AutomationElement.AutomationIdProperty, "")));

        private static PropertyCondition onScreen = new PropertyCondition(AutomationElement.IsOffscreenProperty, false);

        private static OrCondition withEdit = new OrCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit), System.Windows.Automation.Condition.TrueCondition);
        private static PropertyCondition controlEdit = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit);

        /// <summary>
        /// Contains a list of all elements on the screen
        /// </summary>
        private static List<AutomationElement> allElements;

        /// <summary>
        /// Elements that are valid for LEET search recognition gathered to create hierarchy structure for paths
        /// </summary>
        public static List<AutomationElement> cachedElements;

        /// <summary>
        /// 
        /// </summary>
        //private static List<AutomationElement> automationCache;
        private static Dictionary<AutomationElement, string> automationCache;

        /// <summary>
        /// Cache element for ElementState class
        /// </summary>
        public static IDictionary<AutomationElement, IDictionary<string, string>> cache;

        /// <summary>
        /// Cache element companion of synced paths for output to files
        /// </summary>
        public static Dictionary<string, int> path;

        /// <summary>
        /// LEET hack moduals for pattern checking
        /// </summary>
        private static IList<ILEETHackModule> hacks;

        /// <summary>
        /// Full path of current search element when searching with Find
        /// </summary>
        private static List<PathElement> fullpath;

        /// <summary>
        /// Ensures all elements are unique and required info is stored for location and full hierarchical path.
        /// </summary>
        private static IDictionary<int[], AEInfo> rid;

        #endregion

        public WidgetRetriever()
        {
        }

        /// <summary>
        /// Finds the corresponding automation element from descriptive string path given.
        /// This is the initial entry point.
        /// </summary>
        /// <param name="path">Defined path of automation id or only automation id</param>
        /// <param name="rootOfSearch">Root UI automation element of process</param>
        public static AutomationElement Find(String currentpath, AutomationElement rootOfSearch)
        {
            try
            {
                //search by path
                if (currentpath == null || currentpath.Equals(""))
                    return rootOfSearch;
                if (rootOfSearch == null)
                    throw new FailedPathException("Failed to find element \"" + currentpath + "\" lookup yeilded Pathing: " + Environment.NewLine + "Root Element is NULL");

                return GetElementFromCache(currentpath, rootOfSearch);
            }
            catch (InvalidIdentifierException e)
            {
                // search by id/name
                return GetElementById(currentpath, rootOfSearch);
            }
        }

        #region Automation Element Custom Search 2

        public static AutomationElement GetElementFromCache(string key, AutomationElement head)
        {
            AutomationElement ret = null;
            fullpath = PathApplier.Apply(key);
            string cname = fullpath[fullpath.Count - 1].Class;
            string name = fullpath[fullpath.Count - 1].Name;
            string id = fullpath[fullpath.Count - 1].ID;

            //if (name == "" && id == "")
            //    throw new FailedPathException("Invalid path for element \"" + key + "\" lookup yeilded Pathing: " + Environment.NewLine + "Root Automation Id/Name:  "
            //                                        + (string.IsNullOrEmpty(head.Current.AutomationId) ? "\"\"" : head.Current.AutomationId) + "/" + head.Current.Name + Environment.NewLine
            //                                        + "\tElement Identifier Searched: " + key);

            //DateTime end = DateTime.Now.AddMilliseconds(100000);
            //while (DateTime.Now < end && ret == null)
            //{
            //    FindAllElements(head);
            //    int index = path.Keys.ToList().IndexOf(key);
            //    ret = automationCache.Keys.ToArray()[index];
            //    if (ret != null && ret.Current.Name == name && ret.Current.AutomationId == id)
            //        return ret;
            //    else
            //        ret = null;
            //}


            IList<ILEETHackModule> h = null;
            IDictionary<AutomationElement, IDictionary<string, string>> c;

            FindAllElements(head, h, out c);
            if (path.Keys.Contains(key))
                return rid.Values.ToArray()[path.Keys.ToList().IndexOf(key)].AE;
            else if (path.Keys.Contains(PathApplier.AppliedString(fullpath)))
                return rid.Values.ToArray()[path.Keys.ToList().IndexOf(PathApplier.AppliedString(fullpath))].AE;

            return GetElementById((id == "" ? name : id), head);
        }

        /// <summary>
        /// Queues ALL visible widgets on the screen for the next event to take place.
        /// This will allow for the state of widgets to change while retriving the prior queued value.
        /// </summary>
        public static void FindAllElements(AutomationElement root)
        {
            path = new Dictionary<string, int>();
            automationCache = new Dictionary<AutomationElement, string>();
            allElements = new List<AutomationElement>();
            InitializeCache(root);

            try
            {
                AddCurrentToCache(root, 0);

            }
            catch { }
            int index = allElements.Count;
            while (allElements.Count > 0)
            {
                try
                {
                    foreach (AutomationElement aut in allElements)
                        if (aut.Current.Name != "" || aut.Current.AutomationId != "")
                            AddCurrentToCache(aut, 0);
                }
                catch { }
                if (allElements.Count == index)
                    break;
                index = allElements.Count;
            }
        }

        /// <summary>
        /// Initializes queues for computation.
        /// </summary>
        private static void InitializeCache(AutomationElement root)
        {
            AutomationElementCollection coll = root.FindAll(TreeScope.Descendants, onScreen);
            AutomationElementCollection temp = root.FindAll(TreeScope.Descendants, onScreen);
            while (coll.Count != temp.Count)
            {
                coll = temp;
                temp = root.FindAll(TreeScope.Descendants, onScreen);
            }

            foreach (AutomationElement child in coll)
                allElements.Add(child);
        }

        /// <summary>
        /// Determines whether the widget is to be added to the queue.
        /// </summary>
        private static void AddCurrentToCache(AutomationElement ae, int parentmarker)
        {
            string name = ae.Current.Name.Trim();
            string id = ae.Current.AutomationId.Trim();
            //string elem = "";

            //if (!string.IsNullOrEmpty(id))
            //    elem = id;
            //else if (!string.IsNullOrEmpty(name))
            //    elem = name;
            //else
            //{
            //    allElements.Remove(ae);
            //    IterateCurrentForCache(ae, parentmarker);
            //    return;
            //}

            if (!automationCache.Keys.Contains(ae))
            {
                string newpath = "";
                if (parentmarker > 0)
                    newpath = path.Keys.ToArray()[parentmarker] + ", ";
                newpath += "(" + id + "; " + name + "; " + ae.Current.ClassName + ")";

                allElements.Remove(ae);
                path.Add(newpath, parentmarker);
                automationCache.Add(ae, newpath);

                parentmarker = path.Count - 1;
                IterateCurrentForCache(ae, parentmarker);
            }
        }

        /// <summary>
        /// Iterate through current widgets direct children.
        /// </summary>
        private static void IterateCurrentForCache(AutomationElement ae, int parentmarker)
        {
            //AutomationElementCollection childcollect = ae.FindAll(TreeScope.Descendants, System.Windows.Automation.Condition.TrueCondition);
            AutomationElementCollection childcollect = ae.FindAll(TreeScope.Children, onScreen);
            foreach (AutomationElement child in childcollect)
                AddCurrentToCache(child, parentmarker);
        }

        #endregion

        //BEST Replay Result thus far.
        #region Automation Element Custom Cache Search

        /// <summary>
        /// Retrieves element from a custom cache that traces the hierarchical structure and locations of elements.
        /// </summary>
        private static AutomationElement GetElementFromCache2(string key, AutomationElement head)
        {
            AutomationElement ret = null;
            //InitializeCollection(head);
            InitializeColl(head);
            fullpath = PathApplier.Apply(key);
            string cname = fullpath[fullpath.Count - 1].Class;
            string name = fullpath[fullpath.Count - 1].Name;
            string id = fullpath[fullpath.Count - 1].ID;

            if (name == "" && id == "")
                throw new FailedPathException("Invalid path for element \"" + key + "\" lookup yeilded Pathing: " + Environment.NewLine + "Root Automation Id/Name:  "
                                                    + (string.IsNullOrEmpty(head.Current.AutomationId) ? "\"\"" : head.Current.AutomationId) + "/" + head.Current.Name + Environment.NewLine
                                                    + "\tElement Identifier Searched: " + key);

            DateTime end = DateTime.Now.AddMilliseconds(100000);
            while (DateTime.Now < end && ret == null)
            {
                cachedElements = new List<AutomationElement>();
                path = new Dictionary<string, int>();

                FindAllAndCreateCache(head);

                if (cachedElements[cachedElements.Count - 1].Current.Name == name && cachedElements[cachedElements.Count - 1].Current.AutomationId == id)
                    ret = cachedElements[cachedElements.Count - 1];
                else
                {
                    if (id != "")
                        ret = GetElementById(id, head);
                    if (ret == null && name != "")
                        ret = GetElementById(name, head);
                }
                fullpath = PathApplier.Apply(key);
            }

            if (ret == null)
            {
                if (id != "")
                    ret = GetElementById(id, head);
                if (ret == null && name != "")
                    ret = GetElementById(name, head);
                if (ret == null && cname != "")
                    ret = GetElementByClass(fullpath[fullpath.Count - 1], head);
                if (ret == null)
                    throw new FailedPathException("Failed to find element \"" + key + "\" lookup yeilded Pathing: " + Environment.NewLine + "Root Automation Id/Name:  "
                                                    + (string.IsNullOrEmpty(head.Current.AutomationId) ? "\"\"" : head.Current.AutomationId) + "/" + head.Current.Name + Environment.NewLine
                                                    + "\tElement Identifier Searched: " + key);
            }

            fullpath = null;
            return ret;
        }

        /// <summary>
        /// Initializes queues for computation.
        /// </summary>
        private static void InitializeColl(AutomationElement root)
        {
            cache = new Dictionary<AutomationElement, IDictionary<string, string>>();
            path = new Dictionary<string, int>();
            allElements = new List<AutomationElement>();

            AutomationElementCollection coll = root.FindAll(TreeScope.Descendants, System.Windows.Automation.Condition.TrueCondition);
            AutomationElementCollection temp = root.FindAll(TreeScope.Descendants, System.Windows.Automation.Condition.TrueCondition);
            while (coll.Count != temp.Count)
            {
                coll = temp;
                temp = root.FindAll(TreeScope.Descendants, onScreen);
            }
            foreach (AutomationElement child in coll)
                allElements.Add(child);
        }

        /// <summary>
        /// Creates custom cache for all visible elements in SUTs tree structure.
        /// </summary>
        private static void FindAllAndCreateCache(AutomationElement root)
        {
            try
            {
                PollForCurrentElements(root, -1);
                foreach (AutomationElement aut in allElements)
                {
                    if (aut.Current.Name != "" || aut.Current.AutomationId != "")
                        PollForCurrentElements(aut, -1);
                }
            }
            catch { }
        }

        /// <summary>
        /// Adds elements to cache along with its on screen location and complete path.
        /// </summary>
        /// <param name="parentmarker">Index of the current element's parent. Root element should have an index of -1.</param>
        private static void PollForCurrentElements(AutomationElement ae, int parentmarker)
        {
            string name = ae.Current.Name.Trim();
            string id = ae.Current.AutomationId.Trim();
            if (string.IsNullOrEmpty(id) && string.IsNullOrEmpty(name))
            {
                allElements.Remove(ae);
                IterateCurrentCache(ae, parentmarker);
                return;
            }

            if (!cachedElements.Contains(ae))
            {
                string newpath = "";
                if (parentmarker > 0)
                    newpath = path.Keys.ToArray()[parentmarker] + ", ";
                newpath += "(" + id + "; " + name + "; " + ae.Current.ClassName + ")";

                cachedElements.Add(ae);
                path.Add(newpath, parentmarker);
                parentmarker = path.Count - 1;  
                IterateCurrentCache(ae, parentmarker);
            }
        }

        /// <summary>
        /// Iterates over the current elements children elements.
        /// </summary>
        private static void IterateCurrentCache(AutomationElement ae, int parentmarker)
        {
            AutomationElementCollection childcollect = ae.FindAll(TreeScope.Descendants, new AndCondition(new PropertyCondition(AutomationElement.AutomationIdProperty, fullpath[0].ID),
                                                                                                          new PropertyCondition(AutomationElement.NameProperty, fullpath[0].Name)));
            fullpath.RemoveAt(0);

            foreach (AutomationElement child in childcollect)
            {
                PollForCurrentElements(child, parentmarker);
                if (fullpath.Count == 0)
                    return;
            }
        }

        #endregion

        //Captures all displayed elements on the SUT (BEST result)
        #region Automation Element ElementState Info Retrieval Using Children Search

        private static List<AutomationElement> editList;
        /// <summary>
        /// Queues ALL visible widgets on the screen for the next event to take place.
        /// This will allow for the state of widgets to change while retriving the prior queued value.
        /// </summary>
        public static List<AEInfo> FindAllElements(AutomationElement root, IList<ILEETHackModule> hack, out IDictionary<AutomationElement, IDictionary<string, string>> mycache)
        {
            cache = new Dictionary<AutomationElement, IDictionary<string, string>>();
            path = new Dictionary<string, int>();
            allElements = new List<AutomationElement>();
            rid = new Dictionary<int[], AEInfo>();
            editList = new List<AutomationElement>();

            if (root == null)
            {
                mycache = cache;
                return null;
            }

            try
            {
                InitializeCollection(root);
            }
            catch(ArgumentException) { }
            hacks = hack;
            AutomationElementCollection controlType = root.FindAll(TreeScope.Children, controlEdit);
            foreach (AutomationElement ele in controlType)
                editList.Add(ele);

            try
            {
                
                AddCurrentElement(root, 0);
            }
            catch { }
            int index = allElements.Count;
            while (allElements.Count > 0)
            {
                try
                {
                    foreach (AutomationElement aut in allElements)
                        if (aut.Current.Name != "" || aut.Current.AutomationId != "")
                            AddCurrentElement(aut, 0);
                }
                catch { }
                if (allElements.Count == index)
                    allElements.RemoveAt(0);
                index = allElements.Count;
            }

            AutomationElementCollection controls = root.FindAll(TreeScope.Descendants, controlEdit);
            foreach (AutomationElement child in controls)
            {
                try
                {
                    AddCurrentElement(child, 0);
                }
                catch { }
            }

            mycache = cache;
            return rid.Values.ToList();
        }
        
        /// <summary>
        /// Initializes queues for computation.
        /// </summary>
        private static void InitializeCollection(AutomationElement root)
        {
            //AutomationElementCollection coll = root.FindAll(TreeScope.Descendants, onScreen);
            //AutomationElementCollection coll = root.FindAll(TreeScope.Children, onScreen);
            AutomationElementCollection coll = root.FindAll(TreeScope.Descendants, System.Windows.Automation.Condition.TrueCondition);
            //AutomationElementCollection coll = root.FindAll(TreeScope.Children, System.Windows.Automation.Condition.TrueCondition);
            //AutomationElementCollection coll = root.FindAll(TreeScope.Descendants, withEdit);
            Thread.Sleep(10);
            //AutomationElementCollection temp = root.FindAll(TreeScope.Descendants, onScreen);
            //AutomationElementCollection temp = root.FindAll(TreeScope.Children, onScreen);
            AutomationElementCollection temp = root.FindAll(TreeScope.Descendants, System.Windows.Automation.Condition.TrueCondition);
            //AutomationElementCollection temp = root.FindAll(TreeScope.Children, System.Windows.Automation.Condition.TrueCondition);
            //AutomationElementCollection temp = root.FindAll(TreeScope.Descendants, withEdit);
            while (coll.Count != temp.Count)
            {
                coll = temp;
                Thread.Sleep(10);
                //temp = root.FindAll(TreeScope.Descendants, onScreen);
                temp = root.FindAll(TreeScope.Descendants, System.Windows.Automation.Condition.TrueCondition);
                //temp = root.FindAll(TreeScope.Children, System.Windows.Automation.Condition.TrueCondition);
                //temp = root.FindAll(TreeScope.Descendants, withEdit);
            }

            foreach (AutomationElement child in coll)
                allElements.Add(child);

            //AutomationElementCollection controlType = root.FindAll(TreeScope.Descendants, controlEdit);
            //foreach (AutomationElement ele in controlType)
            //{
            //}
        }

        /// <summary>
        /// Determines whether the widget is to be added to the queue.
        /// </summary>
        private static void AddCurrentElement(AutomationElement ae, int parentmarker)
        {
            if (editList.Contains(ae))
                ae = editList[editList.IndexOf(ae)];
            string name = ae.Current.Name.Trim();
            string id = ae.Current.AutomationId.Trim();
            string elem = "";

            if (!string.IsNullOrEmpty(id))
                elem = id;
            else if (!string.IsNullOrEmpty(name))
                elem = name;
            else
            {
                allElements.Remove(ae);
                IterateCurrentElements(ae, parentmarker);
                return;
            }

            if (!rid.Keys.Contains(ae.GetRuntimeId()))
            {
                IDictionary<String, String> properties = AddPatterns(ae, hacks);
                string newpath = "";
                if (parentmarker > 0)
                    newpath = path.Keys.ToArray()[parentmarker] + ", ";
                newpath += "(" + id + "; " + name + "; " + ae.Current.ClassName + ")";

                allElements.Remove(ae);
                path.Add(newpath, parentmarker);
                //int[] rids = ae.GetRuntimeId();
                //rid.Add(rids, new AEInfo(ae, newpath, ae.Current.BoundingRectangle, rids));

                //don't need path dictionary, never call path.Values. parentmarker List is not needed.
                //rid ensures we get all unique elements for referencing, get better results

                try { cache.Add(ae, properties); } catch { }

                parentmarker = path.Count - 1;
                IterateCurrentElements(ae, parentmarker);
            }
            else
            {
                allElements.Remove(ae);
                IterateCurrentElements(ae, parentmarker);
                return;
            }
        }

        /// <summary>
        /// Iterate through current widgets direct children.
        /// </summary>
        private static void IterateCurrentElements(AutomationElement ae, int parentmarker)
        {
            //AutomationElementCollection childcollect = ae.FindAll(TreeScope.Descendants, System.Windows.Automation.Condition.TrueCondition);
            AutomationElementCollection childcollect = ae.FindAll(TreeScope.Children, System.Windows.Automation.Condition.TrueCondition);
            //AutomationElementCollection childcollect = ae.FindAll(TreeScope.Descendants, withEdit);
            //AutomationElementCollection childcollect = ae.FindAll(TreeScope.Children, onScreen);
            //AutomationElementCollection childcollect = ae.FindAll(TreeScope.Descendants, onScreen);

            
            foreach (AutomationElement child in childcollect)
                AddCurrentElement(child, parentmarker);
        }

        #endregion

        #region Using CacheRequest

        public static List<AEInfo> FindAllElements1(AutomationElement root, IList<ILEETHackModule> hack, out IDictionary<AutomationElement, IDictionary<string, string>> mycache)
        {
            cache = new Dictionary<AutomationElement, IDictionary<string, string>>();
            path = new Dictionary<string, int>();
            allElements = new List<AutomationElement>();
            rid = new Dictionary<int[], AEInfo>();

            CacheRequest cr = new CacheRequest();
            cr.AutomationElementMode = AutomationElementMode.Full;
            cr.TreeFilter = Automation.RawViewCondition;
            cr.TreeScope = TreeScope.Subtree;
            cr.Add(AutomationElement.NameProperty);
            cr.Add(AutomationElement.AutomationIdProperty);
            cr.Add(AutomationElement.IsEnabledProperty);
            cr.Add(AutomationElement.BoundingRectangleProperty);

            //cr.Add(ValuePattern.Pattern);
            //cr.Add(ValuePattern.ValueProperty);
            
            //cr.Add(WindowPattern.Pattern);
            //cr.Add(TransformPattern.Pattern);
            //cr.Add(SelectionPattern.Pattern);
            //cr.Add(SelectionItemPattern.Pattern);
            //cr.Add(TogglePattern.Pattern);
            //cr.Add(ScrollPattern.Pattern);
            //cr.Add(RangeValuePattern.Pattern);
            //cr.Add(MultipleViewPattern.Pattern);
            //cr.Add(GridPattern.Pattern);
            //cr.Add(GridItemPattern.Pattern);
            //cr.Add(DockPattern.Pattern);
            //cr.Add(ExpandCollapsePattern.Pattern);
            //cr.Add(TablePattern.Pattern);


            AutomationElementCollection aec;
            //aec = root.FindAll(TreeScope.Descendants, System.Windows.Automation.Condition.TrueCondition);
            using (cr.Activate())
            {
                aec = root.FindAll(TreeScope.Descendants, System.Windows.Automation.Condition.TrueCondition);
                root = root.FindFirst(TreeScope.Element, System.Windows.Automation.Condition.TrueCondition);

            }
            foreach (AutomationElement current in aec)
                allElements.Add(current);

            try
            {
                AddChilds(root, -1);
            }
            catch { }
            while (allElements.Count > 0)
            {
                try
                {
                    foreach (AutomationElement current in allElements)
                        AddChilds(current, 0);
                }
                catch { }
            }

            mycache = cache;
            return rid.Values.ToList();
        }

        private static void AddChilds(AutomationElement ae, int parentmarker)
        {
            if (!rid.Keys.Contains(ae.GetRuntimeId()))
            {
                IDictionary<String, String> properties = AddCachedPatterns(ae, hacks);
                string newpath = "";
                if (parentmarker > 0)
                    newpath = path.Keys.ToArray()[parentmarker] + ", ";
                newpath += "(" + ae.Cached.AutomationId + "; " + ae.Cached.Name + "; " + ae.Current.ClassName + ")";

                allElements.Remove(ae);
                path.Add(newpath, parentmarker);
                //int[] rids = ae.GetRuntimeId();
                //rid.Add(rids, new AEInfo(ae, newpath, ae.Cached.BoundingRectangle));

                try { cache.Add(ae, properties); }
                catch { }

                parentmarker = path.Count - 1;
                AutomationElementCollection coll = ae.CachedChildren;
                foreach (AutomationElement childs in coll)
                    AddChilds(childs, parentmarker);
            }
        }

        #endregion

        #region Standard Searches

        /// <summary>
        /// Secondary search for searching by id only.
        /// </summary>
        public static AutomationElement GetElementById(string identifier, AutomationElement head)
        {
            AutomationElement ae = head.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.AutomationIdProperty, identifier));
            if (ae == null)
                return GetElementByName(identifier, head);
            return ae;
        }

        /// <summary>
        /// Secondary search for searching by name only.
        /// </summary>
        private static AutomationElement GetElementByName(string identifier, AutomationElement head)
        {
            AutomationElement ae = head.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.NameProperty, identifier));
            return ae;
        }

        /// <summary>
        /// Tiertiary search for search by class name as last resort.
        /// </summary>
        private static AutomationElement GetElementByClass(PathElement path, AutomationElement head)
        {
            AutomationElement ae = null;
            AutomationElementCollection aec = head.FindAll(TreeScope.Descendants, new PropertyCondition(AutomationElement.ClassNameProperty, path.Class));
            foreach (AutomationElement childs in aec)
                if (path.Name == childs.Current.Name && path.ID == childs.Current.AutomationId)
                    return childs;
            return ae;
        }

        #endregion

        /// <summary>
        /// Adds related properties for the current element for verification statements.
        /// </summary>
        private static IDictionary<String, String> AddPatterns(AutomationElement ae, IList<ILEETHackModule> hackmod)
        {
            //create elem entry
            IDictionary<String, String> properties = new Dictionary<String, String>();
            bool isImplemented = false;
            object objPattern = null;

            isImplemented = ae.TryGetCurrentPattern(WindowPattern.Pattern, out objPattern);
            if (isImplemented)
            {
                WindowPattern wpattern = objPattern as WindowPattern;
                properties.Add("WindowPattern.Current.CanMaximize", wpattern.Current.CanMaximize.ToString().Trim());
                properties.Add("WindowPattern.Current.CanMinimize", wpattern.Current.CanMinimize.ToString().Trim());
                properties.Add("WindowPattern.Current.IsModal", wpattern.Current.IsModal.ToString().Trim());
                properties.Add("WindowPattern.Current.IsTopmost", wpattern.Current.IsTopmost.ToString().Trim());
                properties.Add("WindowPattern.Current.WindowInteractionState", wpattern.Current.WindowInteractionState.ToString().Trim());
                properties.Add("WindowPattern.Current.WindowVisualState", wpattern.Current.WindowVisualState.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(ValuePattern.Pattern, out objPattern);
            if (isImplemented)
            {
                ValuePattern vpattern = objPattern as ValuePattern;
                properties.Add("ValuePattern.Current.Value", vpattern.Current.Value.ToString().Trim());
                properties.Add("ValuePattern.Current.IsReadOnly", vpattern.Current.IsReadOnly.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(TransformPattern.Pattern, out objPattern);
            if (isImplemented)
            {
                TransformPattern tpattern = objPattern as TransformPattern;

                properties.Add("TransformPattern.Current.CanMove", tpattern.Current.CanMove.ToString().Trim());
                properties.Add("TransformPattern.Current.CanResize", tpattern.Current.CanResize.ToString().Trim());
                properties.Add("TransformPattern.Current.CanRotate", tpattern.Current.CanRotate.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(SelectionPattern.Pattern, out objPattern);
            if (isImplemented)
            {
                SelectionPattern spattern = objPattern as SelectionPattern;
                properties.Add("SelectionPattern.Current.SelectionProperty", spattern.Current.GetSelection().ToString().Trim());
                properties.Add("SelectionPattern.Current.IsSelectionRequired", spattern.Current.IsSelectionRequired.ToString().Trim());
                properties.Add("SelectionPattern.Current.CanSelectMultiple", spattern.Current.CanSelectMultiple.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(SelectionItemPattern.Pattern, out objPattern);
            if (isImplemented)
            {
                SelectionItemPattern sipattern = objPattern as SelectionItemPattern;
                if (sipattern.Current.SelectionContainer != null)
                    properties.Add("SelectionItemPattern.Current.SelectionContainer", sipattern.Current.SelectionContainer.ToString().Trim());
                properties.Add("SelectionItemPattern.Current.IsSelected", sipattern.Current.IsSelected.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(ScrollPattern.Pattern, out objPattern);
            if (isImplemented)
            {
                ScrollPattern spattern = objPattern as ScrollPattern;
                properties.Add("ScrollPattern.Current.HorizontallyScrollable", spattern.Current.HorizontallyScrollable.ToString().Trim());
                properties.Add("ScrollPattern.Current.HorizontalScrollPercent", spattern.Current.HorizontalScrollPercent.ToString().Trim());
                properties.Add("ScrollPattern.Current.HorizontalViewSize", spattern.Current.HorizontalViewSize.ToString().Trim());
                //properties.Add("ScrollPattern.Current.NoScroll", spattern.Current.NoScroll.ToString().Trim());
                properties.Add("ScrollPattern.Current.VerticallyScrollable", spattern.Current.VerticallyScrollable.ToString().Trim());
                properties.Add("ScrollPattern.Current.VerticalScrollPercent", spattern.Current.VerticalScrollPercent.ToString().Trim());
                properties.Add("ScrollPattern.Current.VerticalViewSize", spattern.Current.VerticalViewSize.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(RangeValuePattern.Pattern, out objPattern);
            if (isImplemented)
            {
                RangeValuePattern rvpattern = objPattern as RangeValuePattern;
                properties.Add("RangeValuePattern.Current.IsReadOnly", rvpattern.Current.IsReadOnly.ToString().Trim());
                properties.Add("RangeValuePattern.Current.LargeChange", rvpattern.Current.LargeChange.ToString().Trim());
                properties.Add("RangeValuePattern.Current.Maximum", rvpattern.Current.Maximum.ToString().Trim());
                properties.Add("RangeValuePattern.Current.Minimum", rvpattern.Current.Minimum.ToString().Trim());
                properties.Add("RangeValuePattern.Current.SmallChange", rvpattern.Current.SmallChange.ToString().Trim());
                properties.Add("RangeValuePattern.Current.Value", rvpattern.Current.Value.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(MultipleViewPattern.Pattern, out objPattern);
            if (isImplemented)
            {
                MultipleViewPattern mvpattern = objPattern as MultipleViewPattern;
                properties.Add("MultipleViewPattern.Current.CurrentView", mvpattern.Current.CurrentView.ToString().Trim());
                properties.Add("MultipleViewPattern.Current.SupportedViews", mvpattern.Current.GetSupportedViews().ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(GridPattern.Pattern, out objPattern);
            if (isImplemented)
            {
                GridPattern gpattern = objPattern as GridPattern;
                properties.Add("GridPattern.Current.ColumnCount", gpattern.Current.ColumnCount.ToString().Trim());
                properties.Add("GridPattern.Current.RowCount", gpattern.Current.RowCount.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(GridItemPattern.Pattern, out objPattern);
            if (isImplemented)
            {
                GridItemPattern gipattern = objPattern as GridItemPattern;
                properties.Add("GridItemPattern.Current.Column", gipattern.Current.Column.ToString().Trim());
                properties.Add("GridItemPattern.Current.ColumnSpan", gipattern.Current.ColumnSpan.ToString().Trim());
                properties.Add("GridItemPattern.Current.ContainingGrid", gipattern.Current.ContainingGrid.ToString().Trim());
                properties.Add("GridItemPattern.Current.Row", gipattern.Current.Row.ToString().Trim());
                properties.Add("GridItemPattern.Current.RowSpan", gipattern.Current.RowSpan.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(ExpandCollapsePattern.Pattern, out objPattern);
            if (isImplemented)
            {
                ExpandCollapsePattern ecpattern = objPattern as ExpandCollapsePattern;
                properties.Add("ExpandCollapsePattern.Current.ExpandCollapseState", ecpattern.Current.ExpandCollapseState.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(DockPattern.Pattern, out objPattern);
            if (isImplemented)
            {
                DockPattern dpattern = objPattern as DockPattern;
                properties.Add("DockPattern.Current.DockPosition", dpattern.Current.DockPosition.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(TablePattern.Pattern, out objPattern);
            if (isImplemented)
            {
                TablePattern tpattern = objPattern as TablePattern;
                properties.Add("TablePattern.Current.ColumnHeaders", tpattern.Current.GetColumnHeaders().ToString().Trim());
                properties.Add("TablePattern.Current.RowHeaders", tpattern.Current.GetRowHeaders().ToString().Trim());
                properties.Add("TablePattern.Current.RowOrColumnMajor", tpattern.Current.RowOrColumnMajor.ToString().Trim());
                isImplemented = false;
            }

            properties.Add(".Current.AutomationId", ae.Current.AutomationId);
            properties.Add(".Current.ClassName", ae.Current.ClassName);
            properties.Add(".Current.FrameworkId", ae.Current.FrameworkId);
            properties.Add(".Current.IsEnabled", ae.Current.IsEnabled.ToString().Trim());
            properties.Add(".Current.Name", ae.Current.Name);

            // hacked properties
            if (hackmod != null && ae.Current.AutomationId.EndsWith("_AutomationHack"))
            {
                ValuePattern pattern = ae.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
                foreach (ILEETHackModule hack in hackmod)
                {
                    pattern.SetValue("_____" + hack.HackFlag);
                    properties.Add(hack.PropertyName, pattern.Current.Value.ToString().Trim());
                }
            }

            return properties;
        }

        private static IDictionary<String, String> AddCachedPatterns(AutomationElement ae, IList<ILEETHackModule> hackmod)
        {
            //create elem entry
            IDictionary<String, String> properties = new Dictionary<String, String>();
            bool isImplemented = false;
            object objPattern = null;

            isImplemented = ae.TryGetCurrentPattern(WindowPattern.Pattern, out objPattern);
            if (isImplemented)
            {
                WindowPattern wpattern = objPattern as WindowPattern;
                properties.Add("WindowPattern.Current.CanMaximize", wpattern.Current.CanMaximize.ToString().Trim());
                properties.Add("WindowPattern.Current.CanMinimize", wpattern.Current.CanMinimize.ToString().Trim());
                properties.Add("WindowPattern.Current.IsModal", wpattern.Current.IsModal.ToString().Trim());
                properties.Add("WindowPattern.Current.IsTopmost", wpattern.Current.IsTopmost.ToString().Trim());
                properties.Add("WindowPattern.Current.WindowInteractionState", wpattern.Current.WindowInteractionState.ToString().Trim());
                properties.Add("WindowPattern.Current.WindowVisualState", wpattern.Current.WindowVisualState.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(ValuePattern.Pattern, out objPattern);
            if (isImplemented)
            {
                ValuePattern vpattern = objPattern as ValuePattern;
                properties.Add("ValuePattern.Current.Value", vpattern.Current.Value.ToString().Trim());
                properties.Add("ValuePattern.Current.IsReadOnly", vpattern.Current.IsReadOnly.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(TransformPattern.Pattern, out objPattern);
            if (isImplemented)
            {
                TransformPattern tpattern = objPattern as TransformPattern;

                properties.Add("TransformPattern.Current.CanMove", tpattern.Current.CanMove.ToString().Trim());
                properties.Add("TransformPattern.Current.CanResize", tpattern.Current.CanResize.ToString().Trim());
                properties.Add("TransformPattern.Current.CanRotate", tpattern.Current.CanRotate.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(SelectionPattern.Pattern, out objPattern);
            if (isImplemented)
            {
                SelectionPattern spattern = objPattern as SelectionPattern;
                properties.Add("SelectionPattern.Current.SelectionProperty", spattern.Current.GetSelection().ToString().Trim());
                properties.Add("SelectionPattern.Current.IsSelectionRequired", spattern.Current.IsSelectionRequired.ToString().Trim());
                properties.Add("SelectionPattern.Current.CanSelectMultiple", spattern.Current.CanSelectMultiple.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(SelectionItemPattern.Pattern, out objPattern);
            if (isImplemented)
            {
                SelectionItemPattern sipattern = objPattern as SelectionItemPattern;
                if (sipattern.Current.SelectionContainer != null)
                    properties.Add("SelectionItemPattern.Current.SelectionContainer", sipattern.Current.SelectionContainer.ToString().Trim());
                properties.Add("SelectionItemPattern.Current.IsSelected", sipattern.Current.IsSelected.ToString().Trim());
                isImplemented = false;
            }

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(ScrollPattern.Pattern, out objPattern);
            if (isImplemented)
            {
                ScrollPattern spattern = objPattern as ScrollPattern;
                properties.Add("ScrollPattern.Current.HorizontallyScrollable", spattern.Current.HorizontallyScrollable.ToString().Trim());
                properties.Add("ScrollPattern.Current.HorizontalScrollPercent", spattern.Current.HorizontalScrollPercent.ToString().Trim());
                properties.Add("ScrollPattern.Current.HorizontalViewSize", spattern.Current.HorizontalViewSize.ToString().Trim());
                //properties.Add("ScrollPattern.Current.NoScroll", spattern.Current.NoScroll.ToString().Trim());
                properties.Add("ScrollPattern.Current.VerticallyScrollable", spattern.Current.VerticallyScrollable.ToString().Trim());
                properties.Add("ScrollPattern.Current.VerticalScrollPercent", spattern.Current.VerticalScrollPercent.ToString().Trim());
                properties.Add("ScrollPattern.Current.VerticalViewSize", spattern.Current.VerticalViewSize.ToString().Trim());
                isImplemented = false;
            }

            //objPattern = null;
            //isImplemented = ae.TryGetCurrentPattern(RangeValuePattern.Pattern, out objPattern);
            //if (isImplemented)
            //{
            //    RangeValuePattern rvpattern = objPattern as RangeValuePattern;
            //    properties.Add("RangeValuePattern.Current.IsReadOnly", rvpattern.Current.IsReadOnly.ToString().Trim());
            //    properties.Add("RangeValuePattern.Current.LargeChange", rvpattern.Current.LargeChange.ToString().Trim());
            //    properties.Add("RangeValuePattern.Current.Maximum", rvpattern.Current.Maximum.ToString().Trim());
            //    properties.Add("RangeValuePattern.Current.Minimum", rvpattern.Current.Minimum.ToString().Trim());
            //    properties.Add("RangeValuePattern.Current.SmallChange", rvpattern.Current.SmallChange.ToString().Trim());
            //    properties.Add("RangeValuePattern.Current.Value", rvpattern.Current.Value.ToString().Trim());
            //    isImplemented = false;
            //}

            //objPattern = null;
            //isImplemented = ae.TryGetCurrentPattern(MultipleViewPattern.Pattern, out objPattern);
            //if (isImplemented)
            //{
            //    MultipleViewPattern mvpattern = objPattern as MultipleViewPattern;
            //    properties.Add("MultipleViewPattern.Current.CurrentView", mvpattern.Current.CurrentView.ToString().Trim());
            //    properties.Add("MultipleViewPattern.Current.SupportedViews", mvpattern.Current.GetSupportedViews().ToString().Trim());
            //    isImplemented = false;
            //}

            //objPattern = null;
            //isImplemented = ae.TryGetCurrentPattern(GridPattern.Pattern, out objPattern);
            //if (isImplemented)
            //{
            //    GridPattern gpattern = objPattern as GridPattern;
            //    properties.Add("GridPattern.Current.ColumnCount", gpattern.Current.ColumnCount.ToString().Trim());
            //    properties.Add("GridPattern.Current.RowCount", gpattern.Current.RowCount.ToString().Trim());
            //    isImplemented = false;
            //}

            //objPattern = null;
            //isImplemented = ae.TryGetCurrentPattern(GridItemPattern.Pattern, out objPattern);
            //if (isImplemented)
            //{
            //    GridItemPattern gipattern = objPattern as GridItemPattern;
            //    properties.Add("GridItemPattern.Current.Column", gipattern.Current.Column.ToString().Trim());
            //    properties.Add("GridItemPattern.Current.ColumnSpan", gipattern.Current.ColumnSpan.ToString().Trim());
            //    properties.Add("GridItemPattern.Current.ContainingGrid", gipattern.Current.ContainingGrid.ToString().Trim());
            //    properties.Add("GridItemPattern.Current.Row", gipattern.Current.Row.ToString().Trim());
            //    properties.Add("GridItemPattern.Current.RowSpan", gipattern.Current.RowSpan.ToString().Trim());
            //    isImplemented = false;
            //}

            objPattern = null;
            isImplemented = ae.TryGetCurrentPattern(ExpandCollapsePattern.Pattern, out objPattern);
            if (isImplemented)
            {
                ExpandCollapsePattern ecpattern = objPattern as ExpandCollapsePattern;
                properties.Add("ExpandCollapsePattern.Current.ExpandCollapseState", ecpattern.Current.ExpandCollapseState.ToString().Trim());
                isImplemented = false;
            }

            //objPattern = null;
            //isImplemented = ae.TryGetCurrentPattern(DockPattern.Pattern, out objPattern);
            //if (isImplemented)
            //{
            //    DockPattern dpattern = objPattern as DockPattern;
            //    properties.Add("DockPattern.Current.DockPosition", dpattern.Current.DockPosition.ToString().Trim());
            //    isImplemented = false;
            //}

            //objPattern = null;
            //isImplemented = ae.TryGetCurrentPattern(TablePattern.Pattern, out objPattern);
            //if (isImplemented)
            //{
            //    TablePattern tpattern = objPattern as TablePattern;
            //    properties.Add("TablePattern.Current.ColumnHeaders", tpattern.Current.GetColumnHeaders().ToString().Trim());
            //    properties.Add("TablePattern.Current.RowHeaders", tpattern.Current.GetRowHeaders().ToString().Trim());
            //    properties.Add("TablePattern.Current.RowOrColumnMajor", tpattern.Current.RowOrColumnMajor.ToString().Trim());
            //    isImplemented = false;
            //}

            properties.Add(".Current.AutomationId", ae.Cached.AutomationId);
            properties.Add(".Current.ClassName", ae.Current.ClassName);
            properties.Add(".Current.FrameworkId", ae.Current.FrameworkId);
            properties.Add(".Current.IsEnabled", ae.Cached.IsEnabled.ToString().Trim());
            properties.Add(".Current.Name", ae.Cached.Name);

            // hacked properties
            if (hackmod != null && ae.Current.AutomationId.EndsWith("_AutomationHack"))
            {
                ValuePattern pattern = ae.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
                foreach (ILEETHackModule hack in hackmod)
                {
                    pattern.SetValue("_____" + hack.HackFlag);
                    properties.Add(hack.PropertyName, pattern.Current.Value.ToString().Trim());
                }
            }

            return properties;
        }
    }
}
